// Package service /*总控制
package service

import (
	"encoding/json"
	"log"
	"path"
	"strings"
	"sync"

	cf "godocker/common/functions"
)

var (
	installDir string
	ymlDir     string
	appName    string
	locked     []string
	lockFile   string

	l sync.RWMutex
)

type Service struct {
	app
	linuxGo
	phpFpm
	docker
}

type Services interface {
	New()
}

func (s Service) New() {
	//获取安装目前
	s.getInstallDir()

	str, err := cf.ReadAll(lockFile)
	cf.HandleErr(err, "cf.ReadAll", "")

	err = json.Unmarshal(str, &locked)
	cf.HandleErr(err, "json.Unmarshal", "")

	if len(locked) > 0 {
		s.update()
	} else {
		s.install()
	}
}

func (s Service) install() {
	s.basicInstall()

	s.libInstall()

	s.run()
}

func (s Service) update() {
	log.Println("已安装部分功能，现执行更新操作")

	s.handleDocker()

	s.libInstall()

	s.run()
}

func (s Service) basicInstall() {
	s.checkDir()

	s.handleDocker()

	s.handleApp()
}

func (s Service) libInstall() {
	//协程
	s.goFunc(s.handleLinuxGo, s.handlePhpFpm, s.handleNginx, s.handleMysql)
}

func (s Service) getInstallDir() {
	installDir = cf.Alert("请输入您的安装路径，需要绝对路径:")
	if installDir == "" {
		s.getInstallDir()
	}

	installDir = strings.Replace(installDir, "\\", "/", -1)
	installDir = strings.TrimSuffix(installDir, "/")

	ymlDir = installDir + "/yml"
	appName = path.Base(installDir)

	lockFile = installDir + "/locked.json"
}

func (s Service) checkDir() {
	var (
		ok     bool
		choice string
	)

	//判断文件夹是否是空
	if ok = cf.DirEmpty(installDir); !ok {
		//目标目录不是空的
		choice = cf.Alert("安装目录不是空目录，是否继续安装[y/N]")

		switch choice {
		case "y", "Y":
		case "n", "N":
			s.New()
		default:

		}
	}
}

func (s Service) handleDocker() {

	d := dockerNew()
	d.install()

	s.writeLocked(d.SName)
}

func (s Service) handleApp() {
	a := appNew()
	a.install()

	s.writeLocked(a.SName)
}

func (s Service) handleLinuxGo() {
	lg := linuxGoNew()

	if cf.InArray(lg.SName, locked) {
		return
	}

	lg.install()

	s.writeLocked(lg.SName)
}

func (s Service) handlePhpFpm() {
	php := phpNew()

	if cf.InArray(php.SName, locked) {
		return
	}

	php.install()

	s.writeLocked(php.SName)
}

func (s Service) handleNginx() {
	n := nginxNew()

	if cf.InArray(n.SName, locked) {
		return
	}

	n.install()

	s.writeLocked(n.SName)
}

func (s Service) handleMysql() {
	m := mysqlNew()

	if cf.InArray(m.SName, locked) {
		return
	}

	m.install()

	s.writeLocked(m.SName)
}

func (s Service) handleMongo() {
	m := mongoNew()

	if cf.InArray(m.SName, locked) {
		return
	}

	m.install()

	s.writeLocked(m.SName)
}

func (s Service) run() {
	file := installDir + "/docker-compose.yml"
	cf.ExecShell("docker-compose -f " + file + " up -d")

	bt, err := json.Marshal(locked)
	cf.HandleErr(err, "json.Marshal", "")
	cf.WriteToFileTrunc(lockFile, string(bt))
}

func (s Service) writeLocked(funcName string) {
	l.Lock()
	defer l.Unlock()

	locked = append(locked, funcName)
}

func (s Service) goFunc(f ...func()) {
	var wg sync.WaitGroup
	wg.Add(len(f))

	for i := 0; i < len(f); i++ {
		i := i
		go func(i int) {
			f[i]()
			wg.Done()
		}(i)
	}

	wg.Wait()
}
